package com.hsogoo.avatar.transport;

import com.google.common.collect.Maps;
import com.hsogoo.avatar.NamedThreadFactory;
import com.hsogoo.avatar.enums.AvatarResult;
import com.hsogoo.avatar.model.AvatarRequest;
import com.hsogoo.avatar.model.AvatarResponse;
import com.hsogoo.avatar.utils.ExceptionUtil;

import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

import lombok.extern.slf4j.Slf4j;

/**
 * @Author huangsaigang
 * Created @2018/9/30.
 */
@Slf4j
public class AvatarFuture {

    private static Map<String, AvatarFuture> allAvatarFuture = Maps.newConcurrentMap();
    private Lock lock = new ReentrantLock();
    private Condition condition = lock.newCondition();
    private volatile AvatarResponse response;
    private long startTime = System.currentTimeMillis();
    private String requestId;
    private final long timeout;
    private final static ExecutorService timeoutCheckExecutor = Executors.newSingleThreadExecutor(new NamedThreadFactory("timeout.check.executor", true));

    public AvatarFuture(AvatarRequest request) {
        allAvatarFuture.put(request.getRequestId(), this);
        requestId = request.getRequestId();
        timeout =request.getTimeout();
        timeoutCheckExecutor.submit(new Runnable() {
            @Override
            public void run() {
                while(true){
                    try {
                        Thread.sleep(1000);
                        AvatarFuture af = allAvatarFuture.get(request.getRequestId());
                        if (af == null) {
                            break;
                        }
                        processTimeout(af);
                    } catch (InterruptedException e) {
                        log.warn("[timeout.check.executor] interrupted.");
                    } catch (Throwable t) {
                        if (log.isWarnEnabled()) {
                            log.warn("Check avatar future timeout: {}, will try again...", ExceptionUtil.getDetailMessage(t));
                        }
                    }
                }
            }
        });
    }

    public AvatarResponse get(){
        if(response == null) {
            lock.lock();
            try {
                condition.await();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }finally {
                lock.unlock();
            }
        }
        return response;
    }

    public AvatarResponse get(long timeout){
        if(response == null) {
            lock.lock();
            try {
                condition.await(timeout, TimeUnit.MILLISECONDS);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }finally {
                lock.unlock();
            }
        }
        return response;
    }

    public static void setResponse(AvatarResponse response){
        AvatarFuture af = allAvatarFuture.get(response.getRequestId());
        if (af == null) {
            //可能已经超时，在定时任务中已经被移除
            return;
        }
        Lock lock = af.lock;
        Condition condition = af.condition;
        lock.lock();
        try{
            af.response = response;
            condition.signal();
            allAvatarFuture.remove(af.getRequestId());
        }finally {
            lock.unlock();
        }
    }

    public String getRequestId(){
        return requestId;
    }

    private void processTimeout(AvatarFuture future) {
        if (System.currentTimeMillis() - future.startTime > future.timeout) {
            AvatarResponse response = new AvatarResponse();
            response.setRequestId(future.getRequestId());
            response.setFailResult(AvatarResult.TIMEOUT_EXCEPTION);
            setResponse(response);
        }
    }

}
